package net.aiion.weave.impl.bytecode.asm;

import net.aiion.weave.asm.AnnotationVisitor;
import net.aiion.weave.asm.Attribute;
import net.aiion.weave.asm.ClassVisitor;
import net.aiion.weave.asm.FieldVisitor;
import net.aiion.weave.asm.MethodVisitor;
import net.aiion.weave.asm.Opcodes;
import net.aiion.weave.spi.bytecode.BytecodeEventFactory;

public class BytecodeASMClassVisitor extends ClassVisitor implements Opcodes
{
    private final BytecodeEventFactory mBytecodeEventFactory;
    private final BytecodeASMAction mBytecodeAction;

    private BytecodeASMAnnotationVisitor annotationVisitor;
    private BytecodeASMFieldVisitor fieldVisitor;
    private BytecodeASMMethodVisitor methodVisitor;

    private boolean isCancelled;

    public BytecodeASMClassVisitor(final BytecodeEventFactory aBytecodeEventFactory,
        final BytecodeASMAction aBytecodeAction)
    {
        super(ASM4);
        mBytecodeEventFactory = aBytecodeEventFactory;
        mBytecodeAction = aBytecodeAction;
    }

    public void cancelVisitation() {
        isCancelled = true;

        if (annotationVisitor != null) {
            annotationVisitor.cancelVisitation();
        }

        if (fieldVisitor != null) {
            fieldVisitor.cancelVisitation();
        }

        if (methodVisitor != null) {
            methodVisitor.cancelVisitation();
        }
    }

    /*
     * The methods of this class must be called in the following order: 
     * visit [ visitSource ] [ visitOuterClass ] ( visitAnnotation | visitAttribute )*
     * ( visitInnerClass | visitField | visitMethod )* visitEnd.
     */

    @Override
    public void visit(final int aVersion, final int aModifiers, final String aName, final String aSignature,
        final String aSuperName, final String[] aInterfaceNames)
    {
        if (isCancelled) {
            return;
        }

        mBytecodeAction.processBytecode(mBytecodeEventFactory.createClassStart(aVersion, aModifiers, aName, aSignature,
            aSuperName, aInterfaceNames));
    }

    @Override
    public void visitSource(final String aSourceFileName, final String aDebugInformation) {
        if (isCancelled) {
            return;
        }

        mBytecodeAction.processBytecode(mBytecodeEventFactory.createSource(aSourceFileName, aDebugInformation));
    }

    @Override
    public void visitOuterClass(final String aEnclosingClassName, final String aMethodName,
        final String aMethodDescriptor)
    {
        if (isCancelled) {
            return;
        }

        mBytecodeAction.processBytecode(mBytecodeEventFactory.createOuterClass(aEnclosingClassName, aMethodName,
            aMethodDescriptor));
    }

    @Override
    public AnnotationVisitor visitAnnotation(final String aDescriptor, final boolean aVisible) {
        if (isCancelled) {
            return null;
        }

        mBytecodeAction.processBytecode(mBytecodeEventFactory.createAnnotationStart(aDescriptor, aVisible));

        annotationVisitor = new BytecodeASMAnnotationVisitor(mBytecodeEventFactory, mBytecodeAction);
        return annotationVisitor;
    }

    @Override
    public void visitAttribute(final Attribute aAttribute) {
        if (isCancelled) {
            return;
        }

        mBytecodeAction.processBytecode(mBytecodeEventFactory.createAttribute(aAttribute));
    }

    @Override
    public void visitInnerClass(final String aInnerName, final String aOuterName, final String aInnerSimpleName,
        final int aModifiers)
    {
        if (isCancelled) {
            return;
        }

        mBytecodeAction.processBytecode(mBytecodeEventFactory.createInnerClass(aInnerName, aOuterName,
            aInnerSimpleName, aModifiers));
    }

    @Override
    public FieldVisitor visitField(final int aModifiers, final String aName, final String aDescriptor,
        final String aSignature, final Object aConstantValue)
    {
        if (isCancelled) {
            return null;
        }

        mBytecodeAction.processBytecode(mBytecodeEventFactory.createFieldStart(aModifiers, aName, aDescriptor,
            aSignature, aConstantValue));
        fieldVisitor = new BytecodeASMFieldVisitor(mBytecodeEventFactory, mBytecodeAction);
        return fieldVisitor;
    }

    @Override
    public MethodVisitor visitMethod(final int aModifiers, final String aName, final String aDescriptor,
        final String aSignature, final String[] aExceptionNames)
    {
        if (isCancelled) {
            return null;
        }

        mBytecodeAction.processBytecode(mBytecodeEventFactory.createMethodStart(aModifiers, aName, aDescriptor,
            aSignature, aExceptionNames));
        methodVisitor = new BytecodeASMMethodVisitor(mBytecodeEventFactory, mBytecodeAction);
        return methodVisitor;
    }

    @Override
    public void visitEnd() {
        if (isCancelled) {
            return;
        }

        mBytecodeAction.processBytecode(mBytecodeEventFactory.createClassEnd());
    }
}
